/*
	Copyright (c) 2010, Mathias Myrland, Myrland Systemutvikling (mathias@myrsys.no)
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:
	
	    * Redistributions of source code must retain the above copyright
	      notice, this list of conditions and the following disclaimer.
	      
	    * Redistributions in binary form must reproduce the above copyright
	      notice, this list of conditions and the following disclaimer in the
	      documentation and/or other materials provided with the distribution.
	      
	    * Neither the name of Myrland Systemutvikling nor the
	      names of its contributors may be used to endorse or promote products
	      derived from this software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL Myrland Systemutvikling BE LIABLE FOR ANY
	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CGF_OBJECT_HPP
#define CGF_OBJECT_HPP

#include <cgf/error.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include <string>
#include <iostream>

namespace CGF
{
	
	/**
	*	Class for storing the attributes of an Object	*/
	class Attribute
	{
		public:
			
	};
	/**
	*	Templated attribute function. 
	*	@tparam attType The datatype of this attribute	*/	
	template<class attType, class t_cname>
	class Attribute_T : public Attribute
	{
		attType m_value;
		boost::weak_ptr<t_cname> m_owner;
		public:
			boost::weak_ptr<t_cname> owner() { return m_owner; }
			attType value() 				{ return m_value; }
			void setValue ( attType v )			{ m_value = v; }
			Attribute_T ( attType v, boost::weak_ptr<t_cname> obj ) : m_value ( v ), m_owner ( obj )	{}
	};
	
	class TemplateAttribute : public Attribute
	{
		public: 
			std::string value;
			std::string type;
	};
			
	/**
	*	A templated object base class, which provides basic functionality like a boost::weak_ptr<t_cname> _this-pointer
	*	
	*	@tparam t_cname	The type of the object	*/
	template<typename t_cname>
        class Object
        {
                boost::weak_ptr<t_cname> m_this;
		
		
		
		/**	The name is the unique identifier for this object. It is the ID of the component in any form or fashion.	*/
		std::string m_name;
		
		public:
			
			/**	A set containing all the classes this component belongs to	*/
			boost::unordered_set<std::string> m_classes;
			
			/**
			*	Add an attribute to the object
			*
			*	@tparam 	valuetype 	The valuetype of the attribute we are adding
			*	@param	name		The key name of the attribute
			*	@param	value		The value to add				*/
			template <class valuetype>
			void setAttribute ( std::string attName, valuetype value )
			{
				if ( !m_attributes.count ( attName ) )
				{
					boost::shared_ptr<Attribute> created ( new Attribute_T<valuetype, t_cname> ( value, this->_this().lock() ) );
				
					m_attributes[attName] = created;
				}
				else
				{
					((Attribute_T<valuetype, t_cname>*)m_attributes[attName].get())->setValue ( value );
				}
				//created->setThis ( created );
			}
			
			/**
			*	Adds a pre-created attribute to the object
			*	@param	name	The name of the attribute
			*	@param	att	The attribute	*/
			void addAttribute ( std::string name, boost::shared_ptr<Attribute> att )
			{
				if ( m_attributes.count ( name ) )
					throw Error ( "Attribute " + name + " already added to the object " + m_name + "." );
				
				m_attributes[name] = att;
			}
			
			/**
			*	Add an attribute to the component
			*
			*	@tparam 	valuetype	The valuetype of the attribute we are adding
			*	@param	name		The key name of the attribute	*/
			template <class valuetype>
			valuetype getAttribute ( std::string attName )
			{
				return ((Attribute_T<valuetype, t_cname>*)m_attributes[attName].get())->value();
			}
			
                        typedef boost::shared_ptr<t_cname> ptr;
                        typedef boost::weak_ptr<t_cname> ptr_w;
                        
                        /**
                        *       Returns a new instance of the objecttype        */
                        static ptr newInstance()
                        {
                                ptr ret ( new t_cname );
                                ret->setThis ( ret );
                                return ret;
                        }
			/**
			*	Access function for the m_classes set
			*	@return	A reference to m_classes.	*/
			boost::unordered_set<std::string>& classes() { return m_classes; }
			
			
			
			/**
			*	Add a class to this object
			*	@param	class	The class name to add	*/
			void addClass ( std::string className )
			{
				//	Insert the classname into the set
				m_classes.insert ( className );
			}
			
			/**
			*	Looks up a class name, and returns true if it is present in the classes set.
			*	@param	className	The class name to look up
			*	@return			The boolean value of the count of elements equal to the className parameter	*/
			bool hasClass ( std::string className )
			{
				//	Simply return the count
				return m_classes.count ( className );
			}
			
                        /**
			*       Returns a boost:weak_ptr to this instance of the class if specified. Throws an error if the m_this ptr has not been set
			*
			*       @throws CGF::Error upon failure */
                        boost::weak_ptr<t_cname>& _this() { return m_this; }
			
			
			/**
			*	Getter for the name
			*	@return			The name of the component	*/
			inline const std::string& name() { return m_name; }
			
                        /**
			*	Set the name of the component
			*	@param	n	The name to set	*/
			void setName ( std::string n )
			{
				m_name = n;
			}
			
			
                        void setThis ( boost::weak_ptr<t_cname> th )
                        {
                                m_this = th;
                        }
                        Object() { }
                        virtual ~Object() {}
				
			/**	A map with all the attribute pointers associated with this property	*/
			boost::unordered_map<std::string, boost::shared_ptr<Attribute> > m_attributes;	
        };
	
}//	namespace CGF
#endif
